use super::MaybeDeserOwned;
use std::{
    borrow::Cow,
    collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque},
    ffi::{CStr, CString, OsStr, OsString},
    marker::PhantomData,
    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
    num::{
        NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
        NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8,
    },
    ops::{Bound, Range, RangeFrom, RangeInclusive, RangeTo},
    path::{Path, PathBuf},
    rc::Rc,
    sync::Arc,
    time::{Duration, SystemTime},
};

type BoxedSlice<T> = Box<[T]>;

macro_rules! impl_for_concretes {
    ($($type_name: ty),*) => {
        $(
        unsafe impl MaybeDeserOwned for $type_name {
            const IS_DESER_OWNED: bool = true;
        }
        )*
    };
}
macro_rules! impl_for_one_generic_sized {
    ($($type_name: ident),*) => {
        $(
        unsafe impl<T: MaybeDeserOwned> MaybeDeserOwned for $type_name<T> {
            const IS_DESER_OWNED: bool = T::IS_DESER_OWNED;
        }
        )*
    };
}
macro_rules! impl_for_one_generic_maybe_sized {
    ($($type_name: ident),*) => {
        $(
        unsafe impl<T: MaybeDeserOwned + ?Sized> MaybeDeserOwned for $type_name<T> {
            const IS_DESER_OWNED: bool = T::IS_DESER_OWNED;
        }
        )*
    };
}
macro_rules! impl_for_multi_generics {
    ($($typ: ident: $($generics_name: ident),*);*) => {
        $(
        unsafe impl<$($generics_name: MaybeDeserOwned),*> MaybeDeserOwned for $typ<$($generics_name,)*> {
            const IS_DESER_OWNED: bool = $($generics_name::IS_DESER_OWNED)&&*;
        }
        )*
    };
}
macro_rules! impl_for_tuples {
    ($($($type_name: ident),*);*) => {
        $(
        unsafe impl<$($type_name: MaybeDeserOwned),*> MaybeDeserOwned for ($($type_name,)*) {
            const IS_DESER_OWNED: bool = $($type_name::IS_DESER_OWNED)&&*;
        }
        )*
    };
}

unsafe impl<T: MaybeDeserOwned, const N: usize> MaybeDeserOwned for [T; N] {
    const IS_DESER_OWNED: bool = T::IS_DESER_OWNED;
}

impl_for_tuples!(
    T1;
    T1, T2;
    T1, T2, T3;
    T1, T2, T3, T4;
    T1, T2, T3, T4, T5;
    T1, T2, T3, T4, T5, T6;
    T1, T2, T3, T4, T5, T6, T7;
    T1, T2, T3, T4, T5, T6, T7, T8;
    T1, T2, T3, T4, T5, T6, T7, T8, T9;
    T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
);

impl_for_concretes!(
    (),
    bool,
    char,
    f32,
    f64,
    i8,
    i16,
    i32,
    i64,
    i128,
    isize,
    u8,
    u16,
    u32,
    u64,
    u128,
    usize,
    NonZeroI8,
    NonZeroI16,
    NonZeroI32,
    NonZeroI64,
    NonZeroI128,
    NonZeroIsize,
    NonZeroU8,
    NonZeroU16,
    NonZeroU32,
    NonZeroU64,
    NonZeroU128,
    String,
    CString,
    OsString,
    Box<OsStr>,
    Box<CStr>,
    PathBuf,
    Box<Path>,
    IpAddr,
    SocketAddr,
    Ipv4Addr,
    Ipv6Addr,
    SocketAddrV4,
    SocketAddrV6,
    Duration,
    SystemTime
);

impl_for_one_generic_sized!(
    Range,
    RangeFrom,
    RangeInclusive,
    RangeTo,
    Bound,
    Option,
    Vec,
    BoxedSlice,
    VecDeque,
    LinkedList,
    HashSet,
    BTreeSet,
    BinaryHeap
);

impl_for_one_generic_maybe_sized!(PhantomData, Box);

impl_for_multi_generics!(
    Result: T, E;
    BTreeMap: K, V;
    HashMap: K, V
);

unsafe impl<T: ?Sized> MaybeDeserOwned for Rc<T>
where
    Box<T>: MaybeDeserOwned,
{
    const IS_DESER_OWNED: bool = <Box<T>>::IS_DESER_OWNED;
}

unsafe impl<T: ?Sized> MaybeDeserOwned for Arc<T>
where
    Box<T>: MaybeDeserOwned,
{
    const IS_DESER_OWNED: bool = <Box<T>>::IS_DESER_OWNED;
}

unsafe impl<'a, T: ?Sized> MaybeDeserOwned for &'a T {
    const IS_DESER_OWNED: bool = false;
}

unsafe impl<'a, T> MaybeDeserOwned for Cow<'a, T>
where
    T: ?Sized + ToOwned,
    T::Owned: MaybeDeserOwned,
{
    const IS_DESER_OWNED: bool = T::Owned::IS_DESER_OWNED;
}

#[test]
fn trait_availability_test() {
    assert!(<(Result<(String, usize), i8>, NonZeroI8)>::IS_DESER_OWNED);
    assert!(!<(&[i32], &str, &Path, Box<OsStr>)>::IS_DESER_OWNED);
}
